package com.db.priceApp.service;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.logging.Logger;

import com.db.priceApp.domain.clientDataBasa.ClientProfile;
import com.db.priceApp.domain.clientDataBasa.ClientsBasa;
import com.db.priceApp.network.protocol.Message;
import com.db.priceApp.network.protocol.MessageTranciever;
import com.db.priceApp.network.protocol.MessageType;

public class ClientRequestsProcessor implements Runnable {

	private final BlockingQueue<Message> clientMarketDataRequests;
	private final Set<String> instruments;
	private final ClientsBasa clientBasa;
	private MessageTranciever mt;
	private Logger logger = Logger.getLogger(ClientRequestsProcessor.class
			.getName());

	public ClientRequestsProcessor(
			BlockingQueue<Message> clientMarketDataRequests, Set<String> instr,
			ClientsBasa basa) {

		this.clientMarketDataRequests = clientMarketDataRequests;
		instruments = instr;
		clientBasa = basa;
		mt = new MessageTranciever();
	}

	@Override
	public void run() {
		while (true) {
			try {
				process(clientMarketDataRequests.take());
			} catch (InterruptedException e) {
				logger.warning("fail to take a client's message from the queue");
				continue;
			}
		}

	}

	private void process(Message clientRequest) {

		byte[] marketDataResponse;
		SelectionKey key;
		key = clientRequest.getKey();
		if (!clientBasa.containsClient(key)) {
			// the first message need to be Logon
			userIsLoginedCorrectly(clientRequest);
			return;
		}

		String instrName = clientRequest.getInstrumentName();
		if (!instruments.contains(instrName)) {
			logger.info("There is no " + instrName + " instrument1");
			marketDataResponse = MessageCostructor
					.createRejectMessage("no such instrument " + instrName);
			try {
				mt.transmit(marketDataResponse, (SocketChannel) key.channel());
				return;
			} catch (IOException exception) {
				logger.severe("fail to transfer market data");
				return;
			}
		}

		String login = clientRequest.getLogin();
		if (login == null) {
			logger.warning("error: empty login");
			return;
		}
		ClientProfile client = clientBasa.getClient(login);
		if (client == null) {
			logger.info("there is no client with login %"
					+ clientRequest.getLogin() + "%");
			return;
		}

		switch (clientRequest.getMessageType()) {
		case E:
			client.subscribe(instrName);
			break;
		case U:
			client.unsubscribe(instrName);
			break;
		default:
			break;
		}

	}

	private boolean userIsLoginedCorrectly(Message clientRequest) {
		SelectionKey key = clientRequest.getKey();
		String login;
		byte[] marketDataResponse;

		try {
			if (clientRequest.getMessageType() != MessageType.A) {
				// ERROR

				logger.info("the first message is not a login");
				marketDataResponse = MessageCostructor
						.createRejectMessage("the first message is not a login");

				mt.transmit(marketDataResponse, (SocketChannel) key.channel());

				// disconnect this user
				key.cancel();
				return false;
			} else { // MessageType = LOGON
				login = clientRequest.getLogin();
				if (clientBasa.containsClient(login)) {
					// such login is already been used

					logger.info("login " + login + " is already been taken");
					marketDataResponse = MessageCostructor
							.createLoginRejectMessage("login " + login
									+ " is already been taken");

					mt.transmit(marketDataResponse,
							(SocketChannel) key.channel());

					// disconnect this user
					key.cancel();
					return false;
				} else { // login is correct
					ClientProfile client = new ClientProfile();
					client.setLogin(login);
					client.setSelectionKey(key);
					clientBasa.addClient(client);

					marketDataResponse = MessageCostructor
							.createLoginReportMessage(login);

					mt.transmit(marketDataResponse,
							(SocketChannel) key.channel());

					return true;
				}
			}
		} catch (IOException e) {
			logger.warning("fail to transmit a message to the client");
			return false;
		}

	}

}
